package com.seashop.filter;

import com.alibaba.fastjson.JSONObject;
import com.seashop.response.ResponseBase;
import com.seashop.dao.AdminMapper;
import com.seashop.enums.ResponseEnum;
import com.seashop.redis.RedisZero;
import com.seashop.util.IPUtil;
import com.seashop.util.JWTUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
@Component
public class GatewayGlobalFilter implements GlobalFilter, Ordered {

    @Value(value = "${system.whiteIpList}")
    String whiteIpList;

    @Value(value = "${system.pathList}")
    String pathList;

    @Value(value = "${system.permitThrough}")
    String permitThrough;

    @Autowired
    private RedisZero redisZero;

    @Autowired
    private AdminMapper adminMapper;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String ip = IPUtil.getIpAddress(request);
        String url = request.getURI().getPath();
        log.info("请求地址url:{}", url);
        // 直接放行的接口
        if (permitThrough.contains(url)) {
            return chain.filter(exchange);
        }
        // 白名单ip地址放行
        if (whiteIpList.contains(ip)) {
            return chain.filter(exchange);
        }
        // 校验请求头是否存在Authorization
        String token = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if (StringUtils.isEmpty(token)) {
            return exchange.getResponse().writeWith(Flux.just(this.getBodyBuffer(exchange.getResponse(), ResponseBase.fail(ResponseEnum.TOKEN_EMPTY.getCode(), ResponseEnum.TOKEN_EMPTY.getMsg()))));
        }
        // 校验Authorization是否过期
        if (!redisZero.exists(token)) {
            return exchange.getResponse().writeWith(Flux.just(this.getBodyBuffer(exchange.getResponse(), ResponseBase.fail(ResponseEnum.TOKEN_EXPIRE.getCode(), ResponseEnum.TOKEN_EXPIRE.getMsg()))));
        }
        // manage管理系统
        if (pathList.contains(url)) {
            String admin = "";
            String adminId = "";
            // App 和 Admin 有可能访问同一个接口，先验证 Redis 里的 token,后验证数据库的
            String userId = getUserId(token);
            List<String> adminInfo = checkAdmin(token);
            if (adminInfo.size() > 0) {
                admin = adminInfo.get(0);
                adminId = adminInfo.get(1);
            }

            if (StringUtils.isEmpty(admin) && StringUtils.isEmpty(userId)) {
                return exchange.getResponse().writeWith(Flux.just(this.getBodyBuffer(exchange.getResponse(), ResponseBase.fail(ResponseEnum.TOKEN_EXPIRE.getCode(), ResponseEnum.TOKEN_EXPIRE.getMsg()))));
            }
            return getMono(exchange, chain, userId, admin, adminId);
        }
        return chain.filter(exchange);
    }

    private Mono<Void> getMono(ServerWebExchange exchange, GatewayFilterChain chain, String userId) {
        return getMono(exchange, chain, userId, null, null);
    }

    private Mono<Void> getMono(ServerWebExchange exchange, GatewayFilterChain chain, String userId, String admin, String adminId) {
        URI uri = exchange.getRequest().getURI();
        StringBuilder query = new StringBuilder();
        String originalQuery = uri.getRawQuery();
        //请求头里的参数转换为参数
        if (!query.toString().contains("admin") && !StringUtils.isEmpty(admin)) {
            if (!StringUtils.hasText(query) || query.charAt(query.length() - 1) != '&') {
                query.append('&');
            }
            query.append("admin");
            query.append('=');
            query.append(admin);
        }
        if (!query.toString().contains("adminId") && !StringUtils.isEmpty(adminId)) {
            if (!StringUtils.hasText(query) || query.charAt(query.length() - 1) != '&') {
                query.append('&');
            }
            query.append("adminId");
            query.append('=');
            query.append(adminId);
        }
        try {
            URI newUri = UriComponentsBuilder.fromUri(uri)
                    .replaceQuery(query.toString())
                    .build(true)
                    .toUri();

            ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build();
            return chain.filter(exchange.mutate().request(request).build());
        } catch (RuntimeException ex) {
            throw new IllegalStateException("Invalid URI query: \"" + query.toString() + "\"");
        }
    }

    private DataBuffer getBodyBuffer(ServerHttpResponse response, ResponseBase result) {
        return response.bufferFactory().wrap(JSONObject.toJSONBytes(result));
    }

    private String getUserId(String token) {
        if (StringUtils.isEmpty(token)) {
            return null;
        }
        Object tokenObject = redisZero.get(token);
        if (null == tokenObject) {
            return null;
        }
        return tokenObject.toString();
    }

    private List<String> checkAdmin(String token) {
        List<String> result = new ArrayList<>();
        if (StringUtils.isEmpty(token)) {
            return result;
        }
        String username = JWTUtil.getUsername(token);
        if (StringUtils.isEmpty(username)) {
            return result;
        }
        Map<String, Object> map = adminMapper.getAdmin(username);
        if (null == map || map.size() == 0) {
            return result;
        }
        if (null == map.get("password") || !JWTUtil.verify(token, username, map.get("password").toString())) {
            return result;
        }
        result.add(username);
        result.add(map.get("id").toString());
        return result;
    }

    @Override
    public int getOrder() {
        return 0;
    }

}
